package lz.controls
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TextEvent;
	import flash.system.IME;
	import flash.text.TextField;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.text.TextLineMetrics;
	import flash.ui.Keyboard;
	
	import lz.controls.core.InvalidationType;
	import lz.controls.core.UIComponent;
	import lz.controls.events.ComponentEvent;
	import lz.controls.events.ScrollEvent;
	import lz.controls.managers.IFocusManager;
	import lz.controls.managers.IFocusManagerComponent;
	/**
	 * TextArea 组件是一个带有边框和可选滚动条的多行文本字段。 
	 * TextArea 组件支持 Adobe Flash Player 的 HTML 呈现功能。
	 */
	public class TextArea extends UIComponent implements IFocusManagerComponent
	{
		/**
		 * A reference to the internal text field of the TextArea component.
		 */
		public var textField:TextField;
		/**
		 * @private (protected)
		 */
		protected var _editable:Boolean=true;
		/**
		 * @private (protected)
		 */
		protected var _wordWrap:Boolean=true;
		/**
		 * @private (protected)
		 */
		protected var _horizontalScrollPolicy:String=ScrollPolicy.AUTO;
		/**
		 * @private (protected)
		 */
		protected var _verticalScrollPolicy:String=ScrollPolicy.AUTO;
		/**
		 * @private (protected)
		 */
		protected var _horizontalScrollBar:UIScrollBar;
		/**
		 * @private (protected)
		 */
		protected var _verticalScrollBar:UIScrollBar;
		/**
		 * @private (protected)
		 */
		protected var background:DisplayObject;
		/**
		 * @private (protected)
		 */
		protected var _html:Boolean=false;
		/**
		 * @private (protected)
		 */
		protected var _savedHTML:String;
		/**
		 * @private (protected)
		 */
		protected var textHasChanged:Boolean=false;
		/**
		 * @private
		 */
		private static var defaultStyles:Object={upSkin: "TextArea_upSkin", disabledSkin: "TextArea_disabledSkin", focusRectSkin: null, focusRectPadding: null, textFormat: null, disabledTextFormat: null, textPadding: 3, embedFonts: false};
		/**
		 * @private (protected)
		 */
		protected static const SCROLL_BAR_STYLES:Object={downArrowDisabledSkin: "downArrowDisabledSkin", downArrowDownSkin: "downArrowDownSkin", downArrowOverSkin: "downArrowOverSkin", downArrowUpSkin: "downArrowUpSkin", upArrowDisabledSkin: "upArrowDisabledSkin", upArrowDownSkin: "upArrowDownSkin", upArrowOverSkin: "upArrowOverSkin", upArrowUpSkin: "upArrowUpSkin", thumbDisabledSkin: "thumbDisabledSkin", thumbDownSkin: "thumbDownSkin", thumbOverSkin: "thumbOverSkin", thumbUpSkin: "thumbUpSkin", thumbIcon: "thumbIcon", trackDisabledSkin: "trackDisabledSkin", trackDownSkin: "trackDownSkin", trackOverSkin: "trackOverSkin", trackUpSkin: "trackUpSkin", repeatDelay: "repeatDelay", repeatInterval: "repeatInterval"};
		/**
		 */
		public static function getStyleDefinition():Object
		{
			return UIComponent.mergeStyles(defaultStyles, ScrollBar.getStyleDefinition());
		}
		/**
		 * @private (internal)
		 */
		public static var createAccessibilityImplementation:Function;
		/**
		 * Creates a new TextArea component instance.
		 */
		public function TextArea()
		{
			super();
		}
		/**
		 * Gets a reference to the horizontal scroll bar.
		 */
		public function get horizontalScrollBar():UIScrollBar
		{
			return _horizontalScrollBar;
		}
		/**
		 * Gets a reference to the vertical scroll bar.
		 */
		public function get verticalScrollBar():UIScrollBar
		{
			return _verticalScrollBar;
		}
		/**
		 */
		override public function get enabled():Boolean
		{
			return super.enabled;
		}
		/**
		 * @private (setter)
		 */
		override public function set enabled(value:Boolean):void
		{
			super.enabled=value;
			mouseChildren=enabled; //Disables mouseWheel interaction.
			invalidate(InvalidationType.STATE);
		}
		/**
		 * Gets or sets a string which contains the text that is currently in
		 * the TextInput component. This property contains text that is unformatted
		 * and does not have HTML tags. To retrieve this text formatted as HTML, use
		 * the <code>htmlText</code> property.
		 *
		 * @default ""
		 */
		public function get text():String
		{
			return textField.text;
		}
		/**
		 * @private (setter)
		 */
		public function set text(value:String):void
		{
			if (componentInspectorSetting && value == "")
			{
				return;
			}

			textField.text=value;
			_html=false;
			invalidate(InvalidationType.DATA);
			invalidate(InvalidationType.STYLES);
			textHasChanged=true;
		}
		/**
		 * Gets or sets the HTML representation of the string that the text field contains.
		 *
		 * @default ""
		 */
		public function get htmlText():String
		{
			return textField.htmlText;
		}
		/**
		 * @private (setter)
		 */
		public function set htmlText(value:String):void
		{
			if (componentInspectorSetting && value == "")
			{
				return;
			}
			if (value == "")
			{
				text="";
				return;
			}
			_html=true;
			_savedHTML=value;
			textField.htmlText=value;
			invalidate(InvalidationType.DATA);
			invalidate(InvalidationType.STYLES);
			textHasChanged=true;
		}
		/**
		 * Gets or sets a Boolean value that indicates whether extra white space
		 * is removed from a TextArea component that contains HTML text. Examples
		 * of extra white space in the component include spaces and line breaks.
		 * A value of <code>true</code> indicates that extra white space is removed;
		 * a value of <code>false</code> indicates that extra white space is not removed.
		 *
		 * <p>This property affects only text that is set by using the <code>htmlText</code>
		 * property; it does not affect text that is set by using the <code>text</code> property.
		 * If you use the <code>text</code> property to set text, the <code>condenseWhite</code>
		 * property is ignored.</p>
		 *
		 * <p>If the <code>condenseWhite</code> property is set to <code>true</code>, you
		 * must use standard HTML commands, such as &lt;br&gt; and &lt;p&gt;, to place line
		 * breaks in the text field.</p>
		 *
		 * @default false
		 */
		public function get condenseWhite():Boolean
		{
			return textField.condenseWhite;
		}
		/**
		 * @private (setter)
		 */
		public function set condenseWhite(value:Boolean):void
		{
			textField.condenseWhite=value;
			invalidate(InvalidationType.DATA);
		}
		/**
		 * Gets or sets the scroll policy for the horizontal scroll bar.
		 * This can be one of the following values:
		 */
		public function get horizontalScrollPolicy():String
		{
			return _horizontalScrollPolicy;
		}
		/**
		 * @private (setter)
		 */
		public function set horizontalScrollPolicy(value:String):void
		{
			_horizontalScrollPolicy=value;
			invalidate(InvalidationType.SIZE);
		}
		/**
		 * Gets or sets the scroll policy for the vertical scroll bar.
		 * This can be one of the following values:
		 */
		public function get verticalScrollPolicy():String
		{
			return _verticalScrollPolicy;
		}
		/**
		 * @private (setter)
		 */
		public function set verticalScrollPolicy(value:String):void
		{
			_verticalScrollPolicy=value;
			invalidate(InvalidationType.SIZE);
		}
		/**
		 * Gets or sets the change in the position of the scroll bar thumb, in  pixels, after
		 * the user scrolls the text field horizontally. If this value is 0, the text
		 * field was not horizontally scrolled.
		 *
		 * @default 0
		 */
		public function get horizontalScrollPosition():Number
		{
			return textField.scrollH;
		}
		/**
		 * @private (setter)
		 */
		public function set horizontalScrollPosition(value:Number):void
		{
			// We must force a redraw to ensure that the size is up to date.
			drawNow();
			textField.scrollH=value;
		}
		/**
		 * Gets or sets the change in the position of the scroll bar thumb, in  pixels, after
		 * the user scrolls the text field vertically. If this value is 1, the text
		 * field was not vertically scrolled.
		 *
		 * @default 1
		 */
		public function get verticalScrollPosition():Number
		{
			return textField.scrollV;
		}
		/**
		 * @private (setter)
		 */
		public function set verticalScrollPosition(value:Number):void
		{
			// We must force a redraw to ensure that the size is up to date.
			drawNow();
			textField.scrollV=value;
		}
		/**
		 * Gets the width of the text, in pixels.
		 *
		 * @default 0
		 */
		public function get textWidth():Number
		{
			drawNow();
			return textField.textWidth;
		}
		/**
		 * Gets the height of the text, in pixels.
		 *
		 * @default 0
		 */
		public function get textHeight():Number
		{
			drawNow();
			return textField.textHeight;
		}
		/**
		 * Gets the count of characters that the TextArea component contains.
		 *
		 * @default 0
		 */
		public function get length():Number
		{
			return textField.text.length;
		}
		/**
		 * Gets or sets the string of characters that the text field
		 * accepts from a user.
		 *
		 * <p>Note that characters that are not included in this string
		 * are accepted in the text field if they are entered programmatically.</p>
		 *
		 * <p>The characters in the string are read from left to right. You can
		 * specify a character range by using the hyphen (-) character. </p>
		 *
		 * <p>If the value of this property is <code>null</code>, the text field
		 * accepts all characters. If this property is set to an empty string (""),
		 * the text field accepts no characters. </p>
		 *
		 * <p>If the string begins with a caret (^) character, all characters
		 * are initially accepted and succeeding characters in the string
		 * are excluded from the set of accepted characters. If the string
		 * does not begin with a caret (^) character, no characters are
		 * initially accepted and succeeding characters in the string are
		 * included in the set of accepted characters.</p>
		 *
		 * @default null
		 */
		public function get restrict():String
		{
			return textField.restrict;
		}
		/**
		 * @private (setter)
		 */
		public function set restrict(value:String):void
		{
			if (componentInspectorSetting && value == "")
			{
				value=null;
			}
			textField.restrict=value;
		}
		/**
		 * Gets or sets the maximum number of characters that a user can enter
		 * in the text field.
		 *
		 * @default 0
		 */
		public function get maxChars():int
		{
			return textField.maxChars;
		}
		/**
		 * @private (setter)
		 */
		public function set maxChars(value:int):void
		{
			textField.maxChars=value;
		}
		/**
		 * Gets the maximum value of the <code>horizontalScrollPosition</code> property.
		 *
		 * @default 0
		 */
		public function get maxHorizontalScrollPosition():int
		{
			return textField.maxScrollH;
		}
		/**
		 * Gets the maximum value of the <code>verticalScrollPosition</code> property.
		 *
		 * @default 1
		 */
		public function get maxVerticalScrollPosition():int
		{
			return textField.maxScrollV;
		}
		/**
		 * Gets or sets a Boolean value that indicates whether the text
		 * wraps at the end of the line. A value of <code>true</code>
		 * indicates that the text wraps; a value of <code>false</code>
		 * indicates that the text does not wrap.
		 *
		 * @default true
		 */
		public function get wordWrap():Boolean
		{
			return _wordWrap;
		}
		/**
		 * @private (setter)
		 */
		public function set wordWrap(value:Boolean):void
		{
			_wordWrap=value;
			invalidate(InvalidationType.STATE);
		}
		/**
		 * Gets the index position of the first selected character in a selection of one or more
		 * characters.
		 *
		 * <p>The index position of a selected character is zero-based and calculated
		 * from the first character that appears in the text area. If there is no selection,
		 * this value is set to the position of the caret.</p>
		 *
		 * @default 0
		 */
		public function get selectionBeginIndex():int
		{
			return textField.selectionBeginIndex;
		}
		/**
		 * Gets the index position of the last selected character in a selection of one or more
		 * characters.
		 *
		 * <p>The index position of a selected character is zero-based and calculated
		 * from the first character that appears in the text area. If there is no selection,
		 * this value is set to the position of the caret.</p>
		 *
		 * @default 0
		 */
		public function get selectionEndIndex():int
		{
			return textField.selectionEndIndex;
		}
		/**
		 * Gets or sets a Boolean value that indicates whether the TextArea component
		 * instance is the text field for a password. A value of <code>true</code>
		 * indicates that the current instance was created to contain a password;
		 * a value of <code>false</code> indicates that it was not.
		 *
		 * <p>If the value of this property is <code>true</code>, the characters
		 * that the user enters in the text area cannot be seen. Instead,
		 * an asterisk is displayed in place of each character that the
		 * user enters. Additionally, the Cut and Copy commands and their keyboard
		 * shortcuts are disabled to prevent the recovery of a password from
		 * an unattended computer.</p>
		 *
		 * @default false
		 */
		public function get displayAsPassword():Boolean
		{
			return textField.displayAsPassword;
		}
		/**
		 * @private (setter)
		 */
		public function set displayAsPassword(value:Boolean):void
		{
			textField.displayAsPassword=value;
		}
		/**
		 * Gets or sets a Boolean value that indicates whether the user can
		 * edit the text in the component. A value of <code>true</code> indicates
		 * that the user can edit the text that the component contains; a value of <code>false</code>
		 * indicates that it cannot.
		 *
		 * @default true
		 */
		public function get editable():Boolean
		{
			return _editable;
		}
		/**
		 * @private (setter)
		 */
		public function set editable(value:Boolean):void
		{
			_editable=value;
			invalidate(InvalidationType.STATE);
		}
		/**
		 * Gets or sets the mode of the input method editor (IME). The IME makes
		 * it possible for users to use a QWERTY keyboard to enter characters from
		 * the Chinese, Japanese, and Korean character sets.
		 *
		 * <p>Flash sets the IME to the specified mode when the component gets focus,
		 * and restores it to the original value after the component loses focus. </p>
		 *
		 * <p>The flash.system.IMEConversionMode class defines constants for
		 * the valid values for this property. Set this property to <code>null</code> to
		 * prevent the use of the IME with the component.</p>
		 */
		public function get imeMode():String
		{
			return IME.conversionMode;
		}
		/**
		 * @private (protected)
		 */
		public function set imeMode(value:String):void
		{
			_imeMode=value;
		}
		/**
		 * Gets or sets a Boolean value that indicates whether Flash Player
		 * highlights a selection in the text field when the text field
		 * does not have focus.
		 *
		 * If this value is set to <code>true</code> and the text field does not
		 * have focus, Flash Player highlights the selection in gray. If this value
		 * is set to <code>false</code> and the text field does not have focus, Flash
		 * Player does not highlight the selection.
		 *
		 * @default false
		 */
		public function get alwaysShowSelection():Boolean
		{
			return textField.alwaysShowSelection;
		}
		/**
		 * @private (setter)
		 */
		public function set alwaysShowSelection(value:Boolean):void
		{
			textField.alwaysShowSelection=value;
		}
		/**
		 */
		override public function drawFocus(draw:Boolean):void
		{
			if (focusTarget != null)
			{
				focusTarget.drawFocus(draw);
				return;
			}
			super.drawFocus(draw);
		}
		/**
		 * Retrieves information about a specified line of text.
		 *
		 * @param lineIndex The line number for which information is to be retrieved.
		 *
		 * @default null
		 */
		public function getLineMetrics(lineIndex:int):TextLineMetrics
		{
			return textField.getLineMetrics(lineIndex);
		}
		/**
		 * Sets the range of a selection made in a text area that has focus.
		 * The selection range begins at the index that is specified by the start
		 * parameter, and ends at the index that is specified by the end parameter.
		 * The selected text is treated as a zero-based string of characters in which
		 * the first selected character is located at index 0, the second
		 * character at index 1, and so on.
		 *
		 * <p>This method has no effect if the text field does not have focus.</p>
		 *
		 * @param setSelection The index location of the first character in the selection.
		 * @param endIndex The index position of the last character in the selection.
		 */
		public function setSelection(setSelection:int, endIndex:int):void
		{
			textField.setSelection(setSelection, endIndex);
		}
		/**
		 * Appends the specified string after the last character that the TextArea
		 * component contains. This method is more efficient than concatenating two strings
		 * by using an addition assignment on a text property--for example,
		 * <code>myTextArea.text += moreText</code>. This method is particularly
		 * useful when the TextArea component contains a significant amount of
		 * content.
		 */
		public function appendText(text:String):void
		{
			textField.appendText(text);
			invalidate(InvalidationType.DATA);
		}
		/**
		 * @private (protected)
		 */
		override protected function configUI():void
		{
			super.configUI();
			tabChildren=true;

			textField=new TextField();
			addChild(textField);
			updateTextFieldType();

			_verticalScrollBar=new UIScrollBar();
			_verticalScrollBar.name="V";
			_verticalScrollBar.visible=false;
			_verticalScrollBar.focusEnabled=false;
			copyStylesToChild(_verticalScrollBar, SCROLL_BAR_STYLES);
			_verticalScrollBar.addEventListener(ScrollEvent.SCROLL, handleScroll, false, 0, true);
			addChild(_verticalScrollBar);

			_horizontalScrollBar=new UIScrollBar();
			_horizontalScrollBar.name="H";
			_horizontalScrollBar.visible=false;
			_horizontalScrollBar.focusEnabled=false;
			_horizontalScrollBar.direction=ScrollBarDirection.HORIZONTAL;
			copyStylesToChild(_horizontalScrollBar, SCROLL_BAR_STYLES);
			_horizontalScrollBar.addEventListener(ScrollEvent.SCROLL, handleScroll, false, 0, true);
			addChild(_horizontalScrollBar);

			textField.addEventListener(TextEvent.TEXT_INPUT, handleTextInput, false, 0, true);
			textField.addEventListener(Event.CHANGE, handleChange, false, 0, true);
			textField.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown, false, 0, true);

			_horizontalScrollBar.scrollTarget=textField;
			_verticalScrollBar.scrollTarget=textField;
			addEventListener(MouseEvent.MOUSE_WHEEL, handleWheel, false, 0, true);
		}
		/**
		 * @private (protected)
		 */
		protected function updateTextFieldType():void
		{
			textField.type=(enabled && _editable) ? TextFieldType.INPUT : TextFieldType.DYNAMIC;
			textField.selectable=enabled;
			textField.wordWrap=_wordWrap;
			textField.multiline=true;
		}
		/**
		 * @private (protected)
		 */
		protected function handleKeyDown(event:KeyboardEvent):void
		{
			if (event.keyCode == Keyboard.ENTER)
			{
				dispatchEvent(new ComponentEvent(ComponentEvent.ENTER, true));
			}
		}
		/**
		 * @private (protected)
		 */
		protected function handleChange(event:Event):void
		{
			event.stopPropagation(); // so you don't get two change events
			dispatchEvent(new Event(Event.CHANGE, true));
			invalidate(InvalidationType.DATA);
		}
		/**
		 * @private (protected)
		 */
		protected function handleTextInput(event:TextEvent):void
		{
			event.stopPropagation();
			dispatchEvent(new TextEvent(TextEvent.TEXT_INPUT, true, false, event.text));
		}
		/**
		 * @private (protected)
		 */
		protected function handleScroll(event:ScrollEvent):void
		{
			dispatchEvent(event);
		}
		/**
		 * @private (protected)
		 */
		protected function handleWheel(event:MouseEvent):void
		{
			if (!enabled || !_verticalScrollBar.visible)
			{
				return;
			}
			_verticalScrollBar.scrollPosition-=event.delta * _verticalScrollBar.lineScrollSize;
			dispatchEvent(new ScrollEvent(ScrollBarDirection.VERTICAL, event.delta * _verticalScrollBar.lineScrollSize, _verticalScrollBar.scrollPosition));
		}
		/**
		 * @private (protected)
		 */
		protected function setEmbedFont():void
		{
			var embed:Object=getStyleValue("embedFonts");
			if (embed != null)
			{
				textField.embedFonts=embed;
			}
		}
		/**
		 * @private (protected)
		 */
		override protected function draw():void
		{
			if (isInvalid(InvalidationType.STATE))
			{
				updateTextFieldType();
			}

			if (isInvalid(InvalidationType.STYLES))
			{
				setStyles();
				setEmbedFont();
			}

			if (isInvalid(InvalidationType.STYLES, InvalidationType.STATE))
			{
				drawTextFormat();
				drawBackground();
				invalidate(InvalidationType.SIZE, false);
			}

			if (isInvalid(InvalidationType.SIZE, InvalidationType.DATA))
			{
				drawLayout();
			}


			super.draw();
		}
		/**
		 * @private (protected)
		 */
		protected function setStyles():void
		{
			copyStylesToChild(_verticalScrollBar, SCROLL_BAR_STYLES);
			copyStylesToChild(_horizontalScrollBar, SCROLL_BAR_STYLES);
		}
		/**
		 * @private (protected)
		 */
		protected function drawTextFormat():void
		{
			// Apply a default textformat
			var uiStyles:Object=UIComponent.getStyleDefinition();
			var defaultTF:TextFormat=enabled ? uiStyles.defaultTextFormat as TextFormat : uiStyles.defaultDisabledTextFormat as TextFormat;
			textField.setTextFormat(defaultTF);

			var tf:TextFormat=getStyleValue(enabled ? "textFormat" : "disabledTextFormat") as TextFormat;
			if (tf != null)
			{
				textField.setTextFormat(tf);
			}
			else
			{
				tf=defaultTF;
			}
			textField.defaultTextFormat=tf;

			setEmbedFont();
			if (_html)
			{
				textField.htmlText=_savedHTML;
			}
		}
		/**
		 * @private (protected)
		 */
		protected function drawBackground():void
		{
			var bg:DisplayObject=background;
			var styleName:String=(enabled) ? "upSkin" : "disabledSkin";
			background=getDisplayObjectInstance(getStyleValue(styleName));
			if (background != null)
			{
				addChildAt(background, 0);
			}
			if (bg != null && bg != background && contains(bg))
			{
				removeChild(bg);
			}
		}
		/**
		 * @private (protected)
		 */
		protected function drawLayout():void
		{
			var txtPad:Number=Number(getStyleValue("textPadding"));
			textField.x=textField.y=txtPad;
			background.width=width;
			background.height=height;

			// Figure out which scrollbars we need:
			var availHeight:Number=height;
			var vScrollBar:Boolean=needVScroll();
			var availWidth:Number=width - (vScrollBar ? _verticalScrollBar.width : 0);

			var hScrollBar:Boolean=needHScroll();
			if (hScrollBar)
			{
				availHeight-=_horizontalScrollBar.height;
			}
			setTextSize(availWidth, availHeight, txtPad);

			// catch the edge case of the horizontal scroll bar necessitating a vertical one:
			if (hScrollBar && !vScrollBar && needVScroll())
			{
				vScrollBar=true;
				availWidth-=_verticalScrollBar.width;
				setTextSize(availWidth, availHeight, txtPad);
			}

			// Size and move the scrollBars
			if (vScrollBar)
			{
				_verticalScrollBar.visible=true;
				_verticalScrollBar.x=width - _verticalScrollBar.width;
				_verticalScrollBar.height=availHeight;
				_verticalScrollBar.visible=true;
				_verticalScrollBar.enabled=enabled;
			}
			else
			{
				_verticalScrollBar.visible=false;
			}

			if (hScrollBar)
			{
				_horizontalScrollBar.visible=true;
				_horizontalScrollBar.y=height - _horizontalScrollBar.height;
				_horizontalScrollBar.width=availWidth;
				_horizontalScrollBar.visible=true;
				_horizontalScrollBar.enabled=enabled;
			}
			else
			{
				_horizontalScrollBar.visible=false;
			}

			updateScrollBars();

			addEventListener(Event.ENTER_FRAME, delayedLayoutUpdate, false, 0, true);
		}
		/**
		 * @private (protected)
		 */
		protected function delayedLayoutUpdate(event:Event):void
		{
			if (textHasChanged)
			{
				textHasChanged=false;
				drawLayout();
				return;
			}
			removeEventListener(Event.ENTER_FRAME, delayedLayoutUpdate);
		}
		/**
		 * @private (protected)
		 */
		protected function updateScrollBars():void
		{
			_horizontalScrollBar.update();
			_verticalScrollBar.update();
			_verticalScrollBar.enabled=enabled;
			_horizontalScrollBar.enabled=enabled;
			_horizontalScrollBar.drawNow();
			_verticalScrollBar.drawNow();
		}
		/**
		 * @private (protected)
		 */
		protected function needVScroll():Boolean
		{
			if (_verticalScrollPolicy == ScrollPolicy.OFF)
			{
				return false;
			}
			if (_verticalScrollPolicy == ScrollPolicy.ON)
			{
				return true;
			}
			return (textField.maxScrollV > 1);
		}
		/**
		 * @private (protected)
		 */
		protected function needHScroll():Boolean
		{
			if (_horizontalScrollPolicy == ScrollPolicy.OFF)
			{
				return false;
			}
			if (_horizontalScrollPolicy == ScrollPolicy.ON)
			{
				return true;
			}
			return (textField.maxScrollH > 0);
		}
		/**
		 * @private (protected)
		 */
		protected function setTextSize(width:Number, height:Number, padding:Number):void
		{
			var w:Number=width - padding * 2;
			var h:Number=height - padding * 2;

			if (w != textField.width)
			{
				textField.width=w;
			}
			if (h != textField.height)
			{
				textField.height=h
			}
		}
		/**
		 * @private (protected)
		 */
		override protected function isOurFocus(target:DisplayObject):Boolean
		{
			return target == textField || super.isOurFocus(target);
		}
		/**
		 * @private (protected)
		 */
		override protected function focusInHandler(event:FocusEvent):void
		{
			setIMEMode(true);

			if (event.target == this)
			{
				stage.focus=textField;
			}
			var fm:IFocusManager=focusManager;
			if (fm)
			{
				if (editable)
				{
					fm.showFocusIndicator=true;
				}
				fm.defaultButtonEnabled=false;
			}
			super.focusInHandler(event);

			if (editable)
			{
				setIMEMode(true);
			}
		}
		/**
		 * @private (protected)
		 */
		override protected function focusOutHandler(event:FocusEvent):void
		{
			var fm:IFocusManager=focusManager;
			if (fm)
			{
				fm.defaultButtonEnabled=true;
			}
			setSelection(0, 0);
			super.focusOutHandler(event);

			if (editable)
			{
				setIMEMode(false);
			}
		}
	}
}
